package com.swotxu.oldwebproject.session;

import com.swotxu.oldwebproject.redis.connection.IRedisConnection;
import com.swotxu.oldwebproject.session.utils.RedisSessionUtil;
import org.springframework.util.CollectionUtils;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;

/**
 * HttpSession 接口实现类
 * 自定义 session，面向 redis操作
 * @Date: 2020/1/9 16:08
 * @Author: xu.hai
 */
public class RedisSession implements HttpSession {
    private final static String CREATED_TIME = "createdTime";
    private final static String LAST_ACCESSED_TIME = "lastAccessedTime";
    private final static String MAX_INACTIVE_INTERVAL = "maxInactiveInterval";

    private String key;
    private String sessionId;
    private long createdTime;
    private long lastAccessedTime;
    private int maxInactiveInterval;

    private IRedisConnection redisConnection;
    private ServletContext servletContext;

    private RedisSession() {}

    private RedisSession(String token, ServletContext servletContext, IRedisConnection redisConnection) {
        this.servletContext = servletContext;
        this.redisConnection = redisConnection;
        // this.sessionId = SessionUtil.generateSessionId();
        this.sessionId = token;
        this.key = RedisSessionUtil.getSessionKey(this.sessionId);
        this.createdTime = System.currentTimeMillis();
        this.lastAccessedTime = this.createdTime;
        saveSession();
    }

    private void saveSession() {
        maxInactiveInterval = RedisSessionUtil.getExpire();
        redisConnection.hset(key, LAST_ACCESSED_TIME, lastAccessedTime);
        redisConnection.hset(key, CREATED_TIME, createdTime);
        redisConnection.hset(key, MAX_INACTIVE_INTERVAL, maxInactiveInterval);
        refresh();
    }

    /**
     * 更新过期时间
     */
    public void refresh() {
        redisConnection.expire(key, getMaxInactiveInterval());
    }

    public static RedisSession createNewSession(String token, ServletContext servletContext, IRedisConnection redisConnection) {
        return new RedisSession(token, servletContext, redisConnection);
    }

    public static RedisSession createExistSession(String token, ServletContext servletContext, IRedisConnection redisConnection) {
        RedisSession session = new RedisSession();
        session.setId(token);
        session.setKey(RedisSessionUtil.getSessionKey(token));
        session.setServletContext(servletContext);
        session.setRedisConnection(redisConnection);
        return session;
    }

    @Override
    public Object getAttribute(String name) {
        return redisConnection.hget(key, RedisSessionUtil.getSessionAttrField(name));
    }

    @Override
    public Enumeration<String> getAttributeNames() {
        return Collections.enumeration(getAttributeKeys());
    }

    private Set<String> getAttributeKeys() {
        Set<String> keys = redisConnection.hkeys(key);
        Set<String> attributeNames = new HashSet<>();
        if (!CollectionUtils.isEmpty(keys)) {
            for (String key : keys) {
                if (key.startsWith(RedisSessionUtil.SESSION_ATTR_PREFIX)) {
                    attributeNames.add(key.substring(RedisSessionUtil.SESSION_ATTR_PREFIX.length()));
                }
            }
        }
        return attributeNames;
    }

    @Override
    public long getCreationTime() {
        return (Long) redisConnection.hget(key, CREATED_TIME);
    }

    private void setId(String id) {
        this.sessionId = id;
    }

    @Override
    public String getId() {
        return sessionId;
    }

    @Override
    public long getLastAccessedTime() {
        return (Long) redisConnection.hget(key, LAST_ACCESSED_TIME);
    }

    public void setLastAccessedTime(long lastAccessedTime) {
        redisConnection.hset(key, LAST_ACCESSED_TIME, lastAccessedTime);
    }

    @Override
    public int getMaxInactiveInterval() {
        Object maxInativeInterval = redisConnection.hget(key,MAX_INACTIVE_INTERVAL);
        return maxInativeInterval == null ? 0 : (Integer) maxInativeInterval;
    }

    @Override
    public ServletContext getServletContext() {
        return servletContext;
    }

    @Override
    public HttpSessionContext getSessionContext() {
        return null;
    }

    @Override
    public Object getValue(String name) {
        return getAttribute(name);
    }

    @Override
    public String[] getValueNames() {
        return getAttributeKeys().toArray(new String[0]);
    }

    /**
     * 删除 redis
     */
    @Override
    public void invalidate() {
        redisConnection.del(key);
    }

    @Override
    public boolean isNew() {
        return !redisConnection.exists(key);
    }

    @Override
    public void putValue(String name, Object value) {
        setAttribute(name, value);
    }

    /**
     * 删除指定 key.
     * @param name
     */
    @Override
    public void removeAttribute(String name) {
        redisConnection.hdel(key, RedisSessionUtil.getSessionAttrField(name));
    }

    @Override
    public void removeValue(String name) {
        removeAttribute(name);
    }

    /**
     * 数据存入 redis
     * @param name
     * @param value
     */
    @Override
    public void setAttribute(String name, Object value) {
        redisConnection.hset(key, RedisSessionUtil.getSessionAttrField(name), value);
    }

    @Override
    public void setMaxInactiveInterval(int seconds) {
        redisConnection.hset(key, MAX_INACTIVE_INTERVAL, seconds);
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public IRedisConnection getRedisConnection() {
        return redisConnection;
    }

    public void setRedisConnection(IRedisConnection redisConnection) {
        this.redisConnection = redisConnection;
    }

    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    public boolean isInvalid() {
        return !redisConnection.exists(key);
    }
}
